package org.jsbd.boss.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.jsbd.boss.bto.HttpResult;
import org.jsbd.boss.common.redis.RedisClient;
import org.jsbd.boss.domian.channel.ApkFilter;
import org.jsbd.boss.domian.channel.ApkInfoVo;
import org.jsbd.boss.domian.channel.ApkLoginTypeConfig;
import org.jsbd.boss.enums.*;
import org.jsbd.boss.mq.UserLogManager;
import org.jsbd.boss.service.impl.CacheParamManager;
import org.jsbd.boss.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * apk管理
 *
 * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
 * @version 1.0.0
 */
@Service
public class ApkManager {

    public static final Logger LOGGER = LoggerFactory.getLogger(ApkManager.class);
    @Resource
    private RedisClient redisClient;
    private Map<Integer, Set<ApkInfoVo>> onlineApkMap = new ConcurrentHashMap<>(4);
    private Map<Integer, Set<ApkInfoVo>> offlineApkMap = new ConcurrentHashMap<>(4);

    private Map<Integer, Set<String>> provinceApkMap = new ConcurrentHashMap<>(4);
    private Map<String, List<ApkLoginTypeConfig>> onlineLoginTypeMap = new ConcurrentHashMap<>(4);

    private Map<String, List<ApkLoginTypeConfig>> offlineLoginTypeMap = new ConcurrentHashMap<>(4);

    @Resource
    private ClientManager clientManager;

    @Resource
    private CacheParamManager cacheParamManager;


    @Resource
    private IApkFilterService apkFilterService;

    @Resource
    private LogManager logManager;


    /**
     * addApksOfProvince:省份可用apk
     *
     * @param provinceId
     * @param apks
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public void addApksOfProvince(int provinceId, List<ApkInfoVo> apks) {
        String provinceKey = CacheKey.PA_KEY + provinceId;
        if (apks == null) {
            apks = new ArrayList<>();
        }
        redisClient.put(provinceKey, apks, 900);
    }


    /**
     * 检查是否已经注册过咪咕
     *
     * @param imsi
     * @return
     */
    private boolean checkHasRegisterMigu(String imsi) {
        String miguRegister = CacheKey.T_S_M_R + "thirdmiguregister" + imsi;
        String register = redisClient.evalGet(miguRegister);
        Object isRegister = redisClient.getHash(CacheKey.MIGU_REGISTER_REOCRD, imsi);
        if (register != null || isRegister != null) {
            return true;
        } else {
            return false;
        }
    }

    private TaskStatusEnum checkLoginType(ApkLoginTypeConfig loginTypeConfig, String imsi, String apk, String mobile) {
        Integer loginType = loginTypeConfig.getLoginType();
        if (loginType.intValue() == LoginTypeEnum.SDK.getType()) {
            if (ClientManager.checkImsi(imsi) != CarrierEnum.MOBILE.getType()) {
                return TaskStatusEnum.SDK_CARRIER_ERROR;
            }
            // 短信发送限制失败次数
            if (clientManager.isUnusefulImsi(imsi)) {
                return TaskStatusEnum.SMS_TIMES_LIMIT;
            }
        }
        // 验证码登录需要有手机号码的
        if (loginType.intValue() == LoginTypeEnum.SMS_CODE.getType()) {
            if (StringUtils.isBlank(mobile)) {
                LOGGER.warn("验证码登录任务参数校验失败 imsi {} apk {} 没有获取到手机号", new Object[]{imsi, apk});
                return TaskStatusEnum.NO_PHONENUM;
            }
        }

        return TaskStatusEnum.SUCCESS;
    }


    private Map<String, Boolean> initSdkFilter(String sdkVer, String appName, FilterTypeEnum filterType) {
        Map<String, Boolean> map = new HashMap<>(4);
        ApkFilter filerVo = new ApkFilter();
        filerVo.setFilterType(filterType.getType());
        boolean canUse = true;
        List<ApkFilter> apkFilterList = apkFilterService.selectAllApkFilterList(filerVo);
        if (apkFilterList != null) {
            for (ApkFilter filter : apkFilterList) {
                String keyword = filter.getSdkFilterKeyword();
                String version = filter.getSdkVersion();
                Integer apkId = filter.getApkId();
                if (StringUtils.isNotBlank(keyword) && StringUtils.isNotBlank(appName)) {
                    String[] keys = keyword.trim().split(" ");
                    for (String key : keys) {
                        if (StringUtils.isNotBlank(key)) {
                            boolean isContain = appName.contains(key);
                            if (filterType == FilterTypeEnum.USEFUL) {
                                if (!isContain) {
                                    canUse = false;
                                }
                            } else {
                                if (isContain) {
                                    canUse = false;
                                }
                            }
                        }
                    }
                }
                if (canUse) {
                    if (StringUtils.isNotBlank(version) && StringUtils.isNotBlank(sdkVer)) {
                        int result = VersonCompareUtil.compareVersion(sdkVer, version.trim());
                        if (filterType == FilterTypeEnum.USEFUL) {
                            if (result < 0) {
                                canUse = false;
                            }
                        } else {
                            if (result >= 0) {
                                canUse = false;
                            }
                        }
                    }
                }
                map.put(apkId.toString(), canUse);
            }
        }
        return map;
    }

    /**
     * 是否新手机
     *
     * @param sysVer
     * @return
     */
    public boolean checkNewSimulator(String sysVer) {
        String targetVersion = (String) cacheParamManager.getParamValue(CacheKey.NEW_SIMULATOR_VERSION);
        if (targetVersion != null && VersonCompareUtil.compareSimulatorVersion(sysVer, targetVersion) >= 0) {
            return true;
        }
        return false;
    }


    public boolean checkNewUserClient(String sysVer, String imsi) {
        //新版本模拟器 只能用新用户
        boolean isNewSimulator = checkNewSimulator(sysVer);
        if (isNewSimulator) {
            //新版本只能用新用户 新用户文件版本一定有记录
            String cacheKey = CacheKey.ONLINE_IMSI_RECORD + "_NEW";
            Long time = (Long) redisClient.getHash(cacheKey, imsi);
            if (time != null) {
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }


    /**
     * checkOffineCanUseApk:选择可用apk
     *
     * @param imsi
     * @param provinceId
     * @param targetBase
     * @param targetApkId
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public TaskStatusEnum checkOffineCanUseApk(String imsi, int provinceId, Integer targetBase, Integer targetApkId) {
        Set<Integer> hasDoApks = clientManager.getImsiHasDoApks(imsi);
        TaskStatusEnum status = TaskStatusEnum.SUCCESS;
        if (hasDoApks != null) {
            Set<ApkInfoVo> apkSet = offlineApkMap.get(provinceId);
            if (apkSet != null && !apkSet.isEmpty()) {
                Integer apkOrBase = 0;
                if (targetBase == ApkBaseEnum.OTHER.getType()) {
                    apkOrBase = targetApkId;
                } else {
                    apkOrBase = targetBase;
                }
                Iterator<ApkInfoVo> apkIterator = apkSet.iterator();
                while (apkIterator.hasNext()) {
                    ApkInfoVo apkInfo = apkIterator.next();
                    Integer apkId = apkInfo.getApkId();
                    if (!hasDoApks.contains(apkOrBase) && apkId.intValue() == targetApkId.intValue()) {
                        // 如果预留了新增量
                        Boolean isReserve = apkInfo.getIsReserve();
                        if (isReserve != null && isReserve) {
                            if (clientManager.hasReserveDoApk(imsi, apkId, apkOrBase)) {
                                return TaskStatusEnum.HAS_DO;
                            }
                        }
                        return TaskStatusEnum.SUCCESS;
                    } else if (hasDoApks.contains(targetApkId) || hasDoApks.contains(targetBase)) {
                        return TaskStatusEnum.HAS_DO;
                    } else {
                        status = TaskStatusEnum.NONE;
                    }
                }
            }
        }
        return status;
    }

    /**
     * selectOnlineApkProvince:选择可用apk
     *
     * @param imsi
     * @param provinceId
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public ApkLoginTypeConfig selectOnlineApkProvince(String imsi, int provinceId, HttpResult httpResult) {
        Set<Integer> hasDoApks = clientManager.getImsiHasDoApks(imsi);
        UserLogManager.addUserHasDoLog(hasDoApks, httpResult);
        if (hasDoApks != null) {
            String appInfo = httpResult.getAppInfo();
            String sdkVerName = httpResult.getSdkVerName();
            Set<ApkInfoVo> apkSet = onlineApkMap.get(provinceId);
            if (apkSet != null && apkSet.size() > 0) {
                Integer carrier = ClientManager.checkImsi(imsi);
                if (carrier != null && carrier != -1) {
                    Map<String, Boolean> useMap = initSdkFilter(sdkVerName, appInfo, FilterTypeEnum.USEFUL);
                    Map<String, Boolean> unUseMap = initSdkFilter(sdkVerName, appInfo, FilterTypeEnum.UNUSEFUL);
                    ApkInfoVo[] apkInfos = apkSet.toArray(new ApkInfoVo[0]);
                    Boolean hasRegister = checkHasRegisterMigu(imsi);
                    String mobile = httpResult.getPhoneNum();
                    apkInfos = (ApkInfoVo[]) RandomUtils.randomObjs(apkInfos);
                    for (ApkInfoVo apkInfo : apkInfos) {
                        //新的客户端 如果版本为新的只能在指定版本的模拟器上使用
                        if (!checkNewUserClient(apkInfo.getSysVer(), imsi)) {
                            continue;
                        }
                        Integer base = apkInfo.getBase();
                        String apk = apkInfo.getMark();
                        Integer apkId = apkInfo.getApkId();
                        String assignDevice = apkInfo.getAssignDevice();
                        if (StringUtils.isNotBlank(assignDevice)) {
                            //指定机型
                            String hsman = httpResult.getHsman();
                            boolean contains = assignDevice.contains(hsman.toLowerCase());
                            if (!contains) {
                                continue;
                            }
                        }

                        String assignSysVer = apkInfo.getAssignSysVer();
                        if (StringUtils.isNotBlank(assignSysVer)) {
                            //指定版本需要大于 否则 过滤
                            String build_version = httpResult.getBuild_version();
                            if (StringUtils.isBlank(build_version)) {
                                continue;
                            } else {
                                //比目标版本小的过滤掉
                                int assVer = Integer.parseInt(assignSysVer);
                                double targetVer = Double.parseDouble(build_version);
                                if (assVer > targetVer) {
                                    continue;
                                }
                            }
                        }

                        List<ApkLoginTypeConfig> loginConfigList = onlineLoginTypeMap.get(apk);
                        if (loginConfigList == null || loginConfigList.isEmpty()) {
                            continue;
                        }

                        Integer miguFlag = apkInfo.getMiguFlag();
                        if (hasRegister && miguFlag != null && miguFlag == 1) {
                            continue;
                        }

                        Boolean useCanDo = useMap.get(apkId.toString());
                        Boolean unUseCanDo = unUseMap.get(apkId.toString());
                        //如果限制的不能用
                        if ((useCanDo != null && !useCanDo) || (unUseCanDo != null && !unUseCanDo)) {
                            continue;
                        }
                        Integer apkOrBase = 0;
                        if (base == ApkBaseEnum.OTHER.getType()) {
                            apkOrBase = apkId;
                        } else {
                            apkOrBase = base;
                        }
                        if (hasDoApks.contains(apkOrBase)) {
                            continue;
                        }
                        String canDoCarrier = apkInfo.getCarrier();
                        // 默认移动
                        if (StringUtils.isBlank(canDoCarrier)) {
                            canDoCarrier = CarrierEnum.MOBILE.getType() + "";
                        }
                        if (!canDoCarrier.contains(carrier.toString())) {
                            continue;
                        }
                        // 如果预留了新增量
                        Boolean isReserve = apkInfo.getIsReserve();
                        if (isReserve != null && isReserve) {
                            if (!clientManager.hasReserveDoApk(imsi, apkId, apkOrBase)) {
                                ApkLoginTypeConfig apkLoginTypeConfig = selectLoginTypeConfig(loginConfigList, mobile, apk, imsi);
                                if (apkLoginTypeConfig != null) {
                                    logManager.printLog(LOGGER, "用户请求apk任务 {} {} {} {}", new Object[]{imsi, String.valueOf(apkOrBase), String.valueOf(apkLoginTypeConfig.getLoginType()), apk});
                                    return apkLoginTypeConfig;
                                }
                            }
                        } else {
                            ApkLoginTypeConfig apkLoginTypeConfig = selectLoginTypeConfig(loginConfigList, mobile, apk, imsi);
                            if (apkLoginTypeConfig != null) {
                                logManager.printLog(LOGGER, "用户请求apk任务 {} {} {} {}", new Object[]{imsi, String.valueOf(apkOrBase), String.valueOf(apkLoginTypeConfig.getLoginType()), apk});
                                return apkLoginTypeConfig;
                            }
                        }
                    }
                }
            } else {
                logManager.printLog(LOGGER, "用户请求没有可用apk {} ,{} ", new Object[]{imsi, httpResult.getSdkVerName()});
            }
        }
        logManager.printLog(LOGGER, "用户请求apk任务为空 {},{} ", new Object[]{imsi, httpResult.getSdkVerName()});
        return null;
    }

    private ApkLoginTypeConfig selectLoginTypeConfig(List<ApkLoginTypeConfig> loginConfigList, String mobile, String apk, String imsi) {
        for (ApkLoginTypeConfig loginConfig : loginConfigList) {
            TaskStatusEnum status = checkLoginType(loginConfig, imsi, apk, mobile);
            if (status == TaskStatusEnum.SUCCESS) {
                loginConfig.setMobile(mobile);
                return loginConfig;
            }
        }
        return null;
    }


    /**
     * checkLoginType:删除不允许使用的登录方式
     *
     * @param infoList
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private Set<ApkInfoVo> checkLoginType(List<ApkInfoVo> infoList, boolean isOnline) {
        Set<ApkInfoVo> apkSet = new HashSet<>();
        if (infoList != null) {
            for (ApkInfoVo apkInfoVo : infoList) {
                Integer loginType = apkInfoVo.getLoginType();
                boolean checkOnline = isOnline(loginType);
                if (isOnline && checkOnline) {
                    apkSet.add(apkInfoVo);
                } else if (!isOnline && !isOnline(loginType)) {
                    apkSet.add(apkInfoVo);
                }
            }
        }
        return apkSet;
    }

    /**
     * isOnline:是否在线登录方式
     *
     * @param loginType
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private boolean isOnline(Integer loginType) {
        if ((loginType == LoginTypeEnum.SDK.getType()
                || loginType == LoginTypeEnum.SMS_CODE.getType()
                || loginType == LoginTypeEnum.NO_NEEED_LOGIN.getType()
                || loginType == LoginTypeEnum.PROXY.getType()
        )) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 查询省份下的可用apk
     */
//    @PostConstruct
    public void updateUsefulApkList() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                long sleepTime = 30;
                while (true) {
                    try {
                        Object refreshTime = cacheParamManager.getParamValue(CacheKey.NEW_TASK_REFRESH);
                        if (refreshTime != null) {
                            sleepTime = Long.parseLong(refreshTime.toString());
                        }
                        Thread.sleep(sleepTime * 1000);
                        Object newTaskFlag = cacheParamManager.getParamValue(CacheKey.NEW_TASK_FLAG);
                        // 开关为0关闭 1开启
                        if (!(newTaskFlag != null && "0".equals(newTaskFlag))) {
                            Set<String> apks = new HashSet<String>();
                            String[] provinces = ProvinceUtils.getProvinceMap().keySet().toArray(new String[0]);
                            for (String provinceStr : provinces) {
                                int province = Integer.parseInt(provinceStr);
                                String key = CacheKey.PA_KEY + province;
                                List<ApkInfoVo> infoList = (List<ApkInfoVo>) redisClient.get(key);
                                Set<String> tempSet = new HashSet<String>();
                                if (infoList != null && infoList.size() > 0) {
                                    Iterator<ApkInfoVo> iterator = infoList.iterator();
                                    while (iterator.hasNext()) {
                                        ApkInfoVo infoVo = iterator.next();
                                        String mark = infoVo.getMark();
                                        apks.add(mark);
                                        tempSet.add(mark);
                                    }
                                }
                                provinceApkMap.put(province, tempSet);
                                offlineApkMap.put(province, checkLoginType(infoList, false));
                                onlineApkMap.put(province, checkLoginType(infoList, true));
                            }

                            for (String apk : apks) {
                                String key = CacheKey.APK_LOGIN_KEY + apk;
                                List<ApkLoginTypeConfig> list = (List<ApkLoginTypeConfig>) redisClient.get(key);
                                List<ApkLoginTypeConfig> onlineLoginList = new ArrayList<ApkLoginTypeConfig>();
                                List<ApkLoginTypeConfig> offlineLoginList = new ArrayList<ApkLoginTypeConfig>();
                                if (list != null) {
                                    for (ApkLoginTypeConfig loginTypeConfig : list) {
                                        Integer loginType = loginTypeConfig.getLoginType();
                                        if (isOnline(loginType)) {
                                            onlineLoginList.add(loginTypeConfig);
                                        } else {
                                            offlineLoginList.add(loginTypeConfig);
                                        }
                                    }
                                }
                                //优先短信验证码
                                if (onlineLoginList.size() > 1) {
                                    onlineLoginList.sort(new Comparator<ApkLoginTypeConfig>() {
                                        @Override
                                        public int compare(ApkLoginTypeConfig o1, ApkLoginTypeConfig o2) {
                                            Integer loginType1 = o1.getLoginType();
                                            LoginTypeEnum loginTypeEnum1 = LoginTypeEnum.valueOf(loginType1);
                                            int priorty1 = loginTypeEnum1.getPriorty();
                                            Integer loginType2 = o2.getLoginType();
                                            LoginTypeEnum loginTypeEnum2 = LoginTypeEnum.valueOf(loginType2);
                                            int priorty2 = loginTypeEnum2.getPriorty();
                                            return priorty1 - priorty2;
                                        }
                                    });
                                }
                                onlineLoginTypeMap.put(apk, onlineLoginList);
                                offlineLoginTypeMap.put(apk, offlineLoginList);
                            }
                        } else {
                            provinceApkMap.clear();
                            onlineLoginTypeMap.clear();
                            offlineLoginTypeMap.clear();
                        }

                    } catch (Exception e) {
                        LOGGER.error("可用包更新", e);
                    }
                }
            }
        }).start();

    }


    /**
     * getRetentTaskApkNum:留存任务天总量
     *
     * @param mark
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public Integer getRetentTaskApkNum(String mark) {
        return (Integer) redisClient.get(CacheKey.AR_TASK_TOTAL + mark);
    }

    /**
     * putRetentTaskApkNum:留存任务总数
     *
     * @param mark
     * @param total
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public void putRetentTaskApkNum(String mark, Integer total) {
        String key = CacheKey.AR_TASK_TOTAL + mark;
        redisClient.put(key, total, 900);
    }

    public Map<Integer, Set<String>> getProvinceApkMap() {
        return provinceApkMap;
    }

    public Map<String, List<ApkLoginTypeConfig>> getOnlineLoginTypeMap() {
        return onlineLoginTypeMap;
    }

    public Map<String, List<ApkLoginTypeConfig>> getOfflineLoginTypeMap() {
        return offlineLoginTypeMap;
    }

}
